//
//  Medium.cpp
//  WiFiMACSim
//
//  Created by Christopher Kemsley on 3/31/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#include "Medium.hpp"
#include "Exception.hpp"
#include "System.hpp"


SimNamespaceBegin




Medium&		Medium :: shared ()
	{
	static Medium s ;
	return s ;
	}






/** Creating mapped connections **/

// "to" can receive connections from "from"
void	Medium :: buildConnection			( ID const & from , ID const & to )
	{
	mRXReceiveSets [to] . add ( from ) ;
	}
void	Medium :: destroyConnection			( ID const & from , ID const & to )
	{
	mRXReceiveSets [to] . add ( from ) ;
	}

// They can each receive eachother's xmission
void	Medium :: buildConnectionPair		( ID const & from , ID const & to )
	{
	buildConnection ( from , to ) ;
	buildConnection ( to , from ) ;
	}
void	Medium :: destroyConnectionPair		( ID const & from , ID const & to )
	{
	destroyConnection ( from , to ) ;
	destroyConnection ( to , from ) ;
	}





/** Processing transmissions **/

void	Medium :: registerMessage			( Msg * message )
	{
	if ( message == 0 )
		ThrowException ( "Medium" , "Attempted to enqueue nil message" , __FILE__,__LINE__ ) ;
	if ( message->firstSlot != UIntegerMax )
		ThrowException ( "Medium" , "Attempted to enqueue already-enqueued message: " << (*message) , __FILE__,__LINE__ ) ;
	
	Msg & m = *message ;
	
	m.retain() ;
	
	m.firstSlot	=	System::shared().currentSlot()+1 ;
	m.lastSlot	=	m.firstSlot + m.duration - 1 ;
	
	mEnqueuedMessages . push_back ( message ) ;
	}

Medium::MsgSet const	Medium :: environment		( ID const & receiverID )
	{
	IDSet const &	txList	=	mRXReceiveSets[receiverID] ;
	
	MsgSet set ;
	
	
	MsgSet::iterator	here	=	mMessagesInFlight.begin() ;
	MsgSet::iterator	end		=	mMessagesInFlight.end() ;
	while ( here != end )
		{
		Msg * m = (*(here++)) ;
		// If the sender is someone we can receive
		// or if it's us
		if ( m->sender==receiverID || txList.contains(m->sender) )
			set . push_back ( m ) ;
		}
	
	
	return set ;
	}




Medium :: Medium ()
	{
	RunLoop::currentRunLoop() . addSource ( this , RunLoop::kModeSystem ) ;
	}
Medium :: ~Medium ()
	{
	// Singleton. Don't unregister.
	//RunLoop::currentRunLoop() . removeSource ( this , RunLoop::kModeSystem ) ;
	}



void	__list_find	( Medium::Msg * m , Medium::MsgSet & l , Medium::MsgSet::iterator & found ) ;
void	__list_find	( Medium::Msg * m , Medium::MsgSet & l , Medium::MsgSet::iterator & found )
	{
	Medium::MsgSet::iterator end = l.end() ;
	found = l.begin() ;
	while ( found != end )
		{
		if ( m == *found )
			return ;
		found ++ ;
		}
	}

void	Medium :: runFromRunLoop ( RunLoop & sender , RunLoop::Mode mode )
	{
	MsgSet				copy		( mMessagesInFlight ) ;
	
	MsgSet::iterator	here	=	copy.begin() ;
	MsgSet::iterator	end		=	copy.end() ;
	
	UInteger			now		=	System::shared().currentSlot() ;
	
	MsgSet::iterator	oHere	=	mMessagesInFlight.begin() ;
	
	while ( here != end )
		{
		Msg & m = * (*(here++)) ;
		if ( m.lastSlot+1 < now ) // if it's REALLY old
			{
			__list_find ( &m , mMessagesInFlight , oHere ) ;
			mMessagesInFlight . erase ( oHere ) ;
			m.release() ;
			}
		else if ( m.lastSlot < now ) // if it just ended
			{
			m.isInFlight = false ;
			
			//std::cout << "Message is over: " << m << "\n" ;
			
			oHere ++ ;
			}
		else
			{
			oHere ++ ;
			}
		}
	
	
	// Add the waiting-for-xmit messages
	here = mEnqueuedMessages.begin() ;
	end  = mEnqueuedMessages.end() ;
	while ( here != end )
		{
		Msg & m = * (*(here++)) ;
		m.isInFlight = true ;
		//std::cout << "Message did start: " << m << "\n" ;
		}
	mMessagesInFlight . insert ( mMessagesInFlight.end() , mEnqueuedMessages.begin() , mEnqueuedMessages.end() ) ;
	mEnqueuedMessages . clear () ;
	}




std::ostream& operator<< ( std::ostream & str , Medium::MsgSet const & set )
	{
	str << "{" ;
	Medium::MsgSet::const_iterator	here	=	set.begin() ;
	Medium::MsgSet::const_iterator	end		=	set.end() ;
	while ( here != end )
		{
		Message & m = **(here++) ;
		str << "\n	" << m ;
		}
	str << "\n	}" ;
	return str ;
	}


SimNamespaceEnd
